// ProgressDialog2.cpp

#include "StdAfx.h"

#include "Common/String/StringConvert.h"

#include "Common/System/Error.h"

#include "ProgressDialog2.h"

#include "resource.h"

#include "../GUI/ExtractRes.h"

#include "Common/String/DataConversions.h"

using namespace Common;
using namespace Common::System;

static const UINT_PTR kTimerID = 3;



static const UINT kTimerElapse =
  #ifdef UNDER_CE
  500
  #else
  100
  #endif
  ;

#include "Common/Lang/LangUtils.h"

#ifdef LANG
static Lang::CIDLangPair kIDLangPairs[] =
{
  { IDCANCEL, 0x02000C00 },
  { IDC_PROGRESS_ELAPSED, 0x02000C01 },
  { IDC_PROGRESS_REMAINING, 0x02000C02 },
  { IDC_PROGRESS_TOTAL, 0x02000C03 },
  { IDC_PROGRESS_SPEED, 0x02000C04 },
  { IDC_PROGRESS_UNPACKED, 0x02000C05 },
  { IDC_PROGRESS_PACKED, 0x02000323 },
  { IDC_PROGRESS_RATIO, 0x02000C06 },
  { IDC_PROGRESS_SPEED, 0x02000C04 },
  { IDC_PROGRESS_FILES, 0x02000320 },
  { IDC_PROGRESS_ERRORS, 0x0308000A },
  { IDC_BUTTON_PROGRESS_PRIORITY, 0x02000C10 },
  { IDC_BUTTON_PAUSE, 0x02000C12 },
  { IDCANCEL, 0x02000711 },
};
#endif

HRESULT CProgressSync::ProcessStopAndPause()
{
  for (;;)
  {
    if (GetStopped())
      return E_ABORT;
    if (!GetPaused())
      break;
    ::Sleep(100);
  }
  return S_OK;
}

HRESULT CProgressSync::SetPosAndCheckPaused(UINT64 completed)
{
  HRESULT hRes = ProcessStopAndPause();
  if(S_OK != hRes)
	return hRes;
  SetPos(completed);
  return S_OK;
}


CProgressDialog2::CProgressDialog2(): _timer(0), CompressingMode(true)
    #ifndef _SFX
    , MainWindow(0)
    #endif
  {
    IconID = -1;
    MessagesDisplayed = false;
    _wasCreated = false;
    _needClose = false;
    _inCancelMessageBox = false;
    _externalCloseMessageWasReceived = false;

    _numPostedMessages = 0;
    _numAutoSizeMessages = 0;
    _errorsWereDisplayed = false;
    _waitCloseByCancelButton = false;
    _cancelWasPressed = false;
    ShowCompressionInfo = true;
    WaitMode = false;
    if (_dialogCreatedEvent.Create(TRUE) != S_OK)
      throw 1334987;
    if (_createDialogEvent.Create(TRUE) != S_OK)
      throw 1334987;
	if (_ThreadAutoFinish.Create() != S_OK)
      throw 1334987;
  }

#ifndef _SFX
CProgressDialog2::~CProgressDialog2()
{
  AddToTitle(_T(""));
}
void CProgressDialog2::AddToTitle(LPCTSTR s)
{
  if (MainWindow != 0)
  {
    CWindow window(MainWindow);
    window.SetWindowText(s + CString(MainTitle));
  }
}

#endif

static const int kTitleFileNameSizeLimit = 36;
static const int kCurrentFileNameSizeLimit = 82;

static void ReduceString(CString &s, int size)
{
  if (s.GetLength() > size)
    s = s.Left(size / 2) + CString(_T(" ... ")) + s.Right(size / 2);
}

void CProgressDialog2::EnableErrorsControls(bool enable)
{
  int cmdShow = enable ? SW_SHOW : SW_HIDE;
  GetDlgItem(IDC_PROGRESS_ERRORS).ShowWindow(cmdShow);
  GetDlgItem(IDC_PROGRESS_ERRORS_VALUE).ShowWindow(cmdShow);
  GetDlgItem(IDC_PROGRESS_LIST).ShowWindow(cmdShow);  
}

LRESULT CProgressDialog2::OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)

{
  _range = (UINT64)(INT64)-1;
  _prevPercentValue = (UINT32)-1;
  _prevElapsedSec = (UINT32)-1;
  _prevRemainingSec = (UINT32)-1;
  _prevSpeed = (UINT32)-1;
  _prevMode = kSpeedBytes;
  _prevTime = ::GetTickCount();
  _elapsedTime = 0;
  _foreground = true;

  m_ProgressBar.Attach(GetDlgItem(IDC_PROGRESS1));
  _messageList.Attach(GetDlgItem(IDC_PROGRESS_LIST));

  _wasCreated = true;
  _dialogCreatedEvent.SetEvent();

  #ifdef LANG
  Lang::LangSetDlgItemsText(HWND(*this), kIDLangPairs, sizeof(kIDLangPairs) / sizeof(kIDLangPairs[0]));
  #endif


  CWindow window(GetDlgItem(IDC_BUTTON_PROGRESS_PRIORITY));
  window.GetWindowText(backgroundString);
  backgroundedString = backgroundString;
  backgroundedString.Replace(_T("&"), _T(""));

  window = GetDlgItem(IDC_BUTTON_PAUSE);
  window.GetWindowText(pauseString);

  foregroundString = Lang::LangStringSpec(IDS_PROGRESS_FOREGROUND, 0x02000C11);
  continueString = Lang::LangStringSpec(IDS_PROGRESS_CONTINUE, 0x02000C13);
  pausedString = Lang::LangStringSpec(IDS_PROGRESS_PAUSED, 0x02000C20);

  SetWindowText(_title);
  SetPauseText();
  SetPriorityText();


  #ifndef UNDER_CE
  _messageList.SetUnicodeFormat(true);
  #endif

  _messageList.InsertColumn(0, _T(""), 30);

  const CString s = Lang::LangStringSpec(IDS_MESSAGES_DIALOG_MESSAGE_COLUMN, 0x02000A80);

  _messageList.InsertColumn(1, s, 600);

  _messageList.SetColumnWidth(0, LVSCW_AUTOSIZE);
  _messageList.SetColumnWidth(1, LVSCW_AUTOSIZE);


  EnableErrorsControls(false);
  RECT rect;
  CWindow wndCancel = GetDlgItem(IDCANCEL);
  wndCancel.GetWindowRect(&rect);
  buttonSizeX = rect.right - rect.left;
  buttonSizeY = rect.bottom - rect.top;
    
  _numReduceSymbols = kCurrentFileNameSizeLimit;
  

  if (!ShowCompressionInfo)
  {
	GetDlgItem(IDC_PROGRESS_PACKED).ShowWindow(SW_HIDE);
    GetDlgItem(IDC_PROGRESS_PACKED_VALUE).ShowWindow(SW_HIDE);
	GetDlgItem(IDC_PROGRESS_RATIO).ShowWindow(SW_HIDE);
	GetDlgItem(IDC_PROGRESS_RATIO_VALUE).ShowWindow(SW_HIDE);    
  }

  if (IconID >= 0)
  {
    HICON icon = LoadIcon(_AtlBaseModule.GetModuleInstance(), MAKEINTRESOURCE(IconID));
    SetIcon(icon, 1);
  }
  _timer = SetTimer(kTimerID, kTimerElapse);
  #ifdef UNDER_CE
  Foreground();
  #endif

  CheckNeedClose();

 CenterWindow(GetParent());
  return TRUE;
}

 int Units_To_Pixels_X(HWND hwnd, int units)
  {
    RECT rect;
    rect.left = 0;
    rect.top = 0;
    rect.right = units;
    rect.bottom = units;
    if (!MapDialogRect(hwnd, &rect))
      return units * 3 / 2;
    return rect.right - rect.left;
  }

LRESULT CProgressDialog2::OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
{
  int xSize = LOWORD(lParam);
	int ySize = HIWORD(lParam);
  int sY;
  int sStep;
  int mx, my;
  {
    RECT rect;
	CWindow wnd = GetDlgItem(IDC_PROGRESS_ELAPSED);
	wnd.GetClientRect(&rect);
    
    mx = rect.left;
    my = rect.top;
    sY = rect.bottom - rect.top;
	wnd = GetDlgItem(IDC_PROGRESS_REMAINING);
	wnd.GetClientRect(&rect);
   
    sStep = rect.top - my;
  }


  InvalidateRect(NULL);

  int xSizeClient = xSize - mx * 2;

  {
    int i;
    for (i = 800; i > 40; i = i * 9 / 10)
      if (Units_To_Pixels_X(m_hWnd, i) <= xSizeClient)
        break;
    _numReduceSymbols = i / 4;
  }

  int yPos = ySize - my - buttonSizeY;

  ChangeSubWindowSizeX(GetDlgItem(IDC_PROGRESS_FILE_NAME), xSize - mx * 2, m_hWnd);
  ChangeSubWindowSizeX(GetDlgItem(IDC_PROGRESS1), xSize - mx * 2, m_hWnd);

  int bSizeX = buttonSizeX;
  int mx2 = mx;
  for (;; mx2--)
  {
    int bSize2 = bSizeX * 3 + mx2 * 2;
    if (bSize2 <= xSizeClient)
      break;
    if (mx2 < 5)
    {
      bSizeX = (xSizeClient - mx2 * 2) / 3;
      break;
    }
  }
  if (bSizeX < 2)
    bSizeX = 2;

  {
    RECT rect;
	CWindow wnd = GetDlgItem(IDC_PROGRESS_LIST);
	wnd.GetClientRect(&rect);
   
    int y = rect.top;
    int ySize2 = yPos - my - y;
    const int kMinYSize = buttonSizeY + buttonSizeY * 3 / 4;
    int xx = xSize - mx * 2;
    if (ySize2 < kMinYSize)
    {
      ySize2 = kMinYSize;
      if (xx > bSizeX * 2)
        xx -= bSizeX;
    }

    _messageList.MoveWindow(mx, y, xx, ySize2);
  }

  {
    int xPos = xSize - mx;
    xPos -= bSizeX;
	
    GetDlgItem(IDCANCEL).MoveWindow(xPos, yPos, bSizeX, buttonSizeY);
    xPos -= (mx2 + bSizeX);
    GetDlgItem(IDC_BUTTON_PAUSE).MoveWindow(xPos, yPos, bSizeX, buttonSizeY);
    xPos -= (mx2 + bSizeX);
    GetDlgItem(IDC_BUTTON_PROGRESS_PRIORITY).MoveWindow(xPos, yPos, bSizeX, buttonSizeY);
  }

  int valueSize;
  int labelSize;
  int padSize;

  labelSize = Units_To_Pixels_X(m_hWnd, MY_PROGRESS_LABEL_UNITS_MIN);
  valueSize = Units_To_Pixels_X(m_hWnd, MY_PROGRESS_VALUE_UNITS);
  padSize = Units_To_Pixels_X(m_hWnd, MY_PROGRESS_PAD_UNITS);
  int requiredSize = (labelSize + valueSize) * 2 + padSize;

  int gSize;
  {
    if (requiredSize < xSizeClient)
    {
      int incr = (xSizeClient - requiredSize) / 3;
      labelSize += incr;
    }
    else
      labelSize = (xSizeClient - valueSize * 2 - padSize) / 2;
    if (labelSize < 0)
      labelSize = 0;

    gSize = labelSize + valueSize;
    padSize = xSizeClient - gSize * 2;
  }

  labelSize = gSize - valueSize;

  UINT IDs[] =
  {
    IDC_PROGRESS_ELAPSED, IDC_PROGRESS_ELAPSED_VALUE,
    IDC_PROGRESS_REMAINING, IDC_PROGRESS_REMAINING_VALUE,
    IDC_PROGRESS_FILES, IDC_PROGRESS_FILES_VALUE,
    IDC_PROGRESS_RATIO, IDC_PROGRESS_RATIO_VALUE,
    IDC_PROGRESS_ERRORS, IDC_PROGRESS_ERRORS_VALUE,
      
    IDC_PROGRESS_TOTAL, IDC_PROGRESS_TOTAL_VALUE,
    IDC_PROGRESS_SPEED, IDC_PROGRESS_SPEED_VALUE,
    IDC_PROGRESS_UNPACKED, IDC_PROGRESS_UNPACKED_VALUE,
    IDC_PROGRESS_PACKED, IDC_PROGRESS_PACKED_VALUE
  };

  yPos = my;
  for (int i = 0; i < sizeof(IDs) / sizeof(IDs[0]); i += 2)
  {
    int x = mx;
    const int kNumColumn1Items = 5 * 2;
    if (i >= kNumColumn1Items)
    {
      if (i == kNumColumn1Items)
        yPos = my;
      x = mx + gSize + padSize;
    }
	GetDlgItem(IDs[i]).MoveWindow(x, yPos, labelSize, sY);
	GetDlgItem(IDs[i + 1]).MoveWindow(x + labelSize, yPos, valueSize, sY);    
    yPos += sStep;
  }
  return false;
}


void CProgressDialog2::SetRange(UINT64 range)
{
  _range = range;
  _previousPos = (UINT64)(INT64)-1;
  _converter.Init(range);
  m_ProgressBar.SetRange32(0, _converter.Count(range));
}

void CProgressDialog2::SetPos(UINT64 pos)
{
  bool redraw = true;
  if (pos < _range && pos > _previousPos)
  {
    if (pos - _previousPos < (_range >> 10))
      redraw = false;
  }
  if(redraw)
  {
    m_ProgressBar.SetPos(_converter.Count(pos)); // Test it for 100%
    _previousPos = pos;
  }
}

static void GetTimeString(UINT64 timeValue, TCHAR *s)
{
  wsprintf(s, TEXT("%02d:%02d:%02d"),
      UINT32(timeValue / 3600),
      UINT32((timeValue / 60) % 60),
      UINT32(timeValue % 60));
}

void CProgressDialog2::ShowSize(int id, UINT64 value)
{
  CString text(_T("\0"));
  if (value != (UINT64)(INT64)-1)
    text = String::ConvertSizeToString(value);
  GetDlgItem(id).SetWindowText(text);  
}

void CProgressDialog2::UpdateStatInfo(bool showAll)
{
  UINT64 total, completed, totalFiles, completedFiles, inSize, outSize;
  bool bytesProgressMode;
  Sync.GetProgress(total, completed, totalFiles, completedFiles, inSize, outSize, bytesProgressMode);

  UINT32 curTime = ::GetTickCount();

  UINT64 progressTotal = bytesProgressMode ? total : totalFiles;
  UINT64 progressCompleted = bytesProgressMode ? completed : completedFiles;

  if (progressTotal != _range)
    SetRange(progressTotal);
  if (progressTotal == (UINT64)(INT64)-1)
  {
    SetPos(0);
    SetRange(progressCompleted);
  }
  else
    SetPos(progressCompleted);

  
  ShowSize(IDC_PROGRESS_TOTAL_VALUE, total);
  
  _elapsedTime += (curTime - _prevTime);
  _prevTime = curTime;

  UINT32 elapsedSec = _elapsedTime / 1000;

  bool elapsedChanged = false;
  if (elapsedSec != _prevElapsedSec)
  {
    TCHAR s[40];
    GetTimeString(elapsedSec, s);
	GetDlgItem(IDC_PROGRESS_ELAPSED_VALUE).SetWindowText(s);    
    _prevElapsedSec = elapsedSec;
    elapsedChanged = true;
  }

  if (elapsedChanged || showAll)
  {
    {
      UINT64 numErrors;

      {
		  Common::System::Threading::CComScopeAutoLock lock(Sync._cs);
        numErrors = Sync.Messages.size();
      }
      if (numErrors > 0)
      {
        UpdateMessagesDialog();
        TCHAR s[40];
        _ui64tot(numErrors, s, 10);
		GetDlgItem(IDC_PROGRESS_ERRORS_VALUE).SetWindowText(s);       
        if (!_errorsWereDisplayed)
        {
          _errorsWereDisplayed = true;
          EnableErrorsControls(true);
        }
      }
    }

    if (completed != 0)
    {

    if (total == (UINT64)(INT64)-1)
    {
	  GetDlgItem(IDC_PROGRESS_REMAINING_VALUE).SetWindowText(_T(""));      
    }
    else
    {
      UINT64 remainingTime = 0;
      if (completed < total)
        remainingTime = _elapsedTime * (total - completed)  / completed;
      UINT64 remainingSec = remainingTime / 1000;
      if (remainingSec != _prevRemainingSec)
      {
        TCHAR s[40];
        GetTimeString(remainingSec, s);
		GetDlgItem(IDC_PROGRESS_REMAINING_VALUE).SetWindowText(s);        
        _prevRemainingSec = remainingSec;
      }
    }
    {
      UINT32 elapsedTime = (_elapsedTime == 0) ? 1 : _elapsedTime;
      UINT64 speedB = (completed * 1000) / elapsedTime;
      UINT64 speedKB = speedB / 1024;
      UINT64 speedMB = speedKB / 1024;
      const UINT32 kLimit1 = 10;
      TCHAR s[40];
      bool needRedraw = false;
      if (speedMB >= kLimit1)
      {
        if (_prevMode != kSpeedMBytes || speedMB != _prevSpeed)
        {
          _ui64tot(speedMB, s, 10);
          lstrcat(s, TEXT(" MB/s"));
          _prevMode = kSpeedMBytes;
          _prevSpeed = speedMB;
          needRedraw = true;
        }
      }
      else if (speedKB >= kLimit1)
      {
        if (_prevMode != kSpeedKBytes || speedKB != _prevSpeed)
        {
          _ui64tot(speedKB, s, 10);
          lstrcat(s, TEXT(" KB/s"));
          _prevMode = kSpeedKBytes;
          _prevSpeed = speedKB;
          needRedraw = true;
        }
      }
      else
      {
        if (_prevMode != kSpeedBytes || speedB != _prevSpeed)
        {
          _ui64tot(speedB, s, 10);
          lstrcat(s, TEXT(" B/s"));
          _prevMode = kSpeedBytes;
          _prevSpeed = speedB;
          needRedraw = true;
        }
      }
      if (needRedraw)
		  GetDlgItem(IDC_PROGRESS_SPEED_VALUE).SetWindowText(s);
    }
    }

    if (total == 0)
      total = 1;
    UINT32 percentValue = (UINT32)(completed * 100 / total);
    CString titleName;
    Sync.GetTitleFileName(titleName);
    if (percentValue != _prevPercentValue || _prevTitleName != titleName)
    {
      _prevPercentValue = percentValue;
      SetTitleText();
      _prevTitleName = titleName;
    }
    
    TCHAR s[64];
    _ui64tot(completedFiles, s, 10);
    if (totalFiles != (UINT64)(INT64)-1)
    {
      lstrcat(s, TEXT(" / "));
     _ui64tot(totalFiles, s + lstrlen(s), 10);
    }
    
    GetDlgItem(IDC_PROGRESS_FILES_VALUE).SetWindowText(s);
    
    const UINT64 packSize   = CompressingMode ? outSize : inSize;
    const UINT64 unpackSize = CompressingMode ? inSize : outSize;

    if (unpackSize == (UINT64)(INT64)-1 && packSize == (UINT64)(INT64)-1)
    {
      ShowSize(IDC_PROGRESS_UNPACKED_VALUE, completed);
	  GetDlgItem(IDC_PROGRESS_PACKED_VALUE).SetWindowText(_T(""));
    }
    else
    {
      ShowSize(IDC_PROGRESS_UNPACKED_VALUE, unpackSize);
      ShowSize(IDC_PROGRESS_PACKED_VALUE, packSize);
      
      if (packSize != (UINT64)(INT64)-1 && unpackSize != (UINT64)(INT64)-1 && unpackSize != 0)
      {
        UINT64 ratio = packSize * 100 / unpackSize;
        _ui64tot(ratio, s, 10);
        lstrcat(s, TEXT("%"));
		GetDlgItem(IDC_PROGRESS_RATIO_VALUE).SetWindowText(s);        
      }
    }
  }


  CString fileName;
  Sync.GetCurrentFileName(fileName);
  if (_prevFileName != fileName)
  {
    int slashPos = fileName.ReverseFind(CHAR_PATH_SEPARATOR);
    CString s1, s2;
    if (slashPos >= 0)
    {
      s1 = fileName.Left(slashPos + 1);
      s2 = fileName.Mid(slashPos + 1);
    }
    else
      s2 = fileName;
    ReduceString(s1, _numReduceSymbols);
    ReduceString(s2, _numReduceSymbols);
    CString s = s1 + _T("\n") + s2;    
	GetDlgItem(IDC_PROGRESS_FILE_NAME).SetWindowText(s);
    _prevFileName == fileName;
  }
}

LRESULT CProgressDialog2::OnTimer(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
  if (Sync.GetPaused())
    return TRUE;

  CheckNeedClose();

  UpdateStatInfo(false);
  return TRUE;
}

struct CWaitCursor
{
  HCURSOR _waitCursor;
  HCURSOR _oldCursor;
  CWaitCursor()
  {
    _waitCursor = LoadCursor(NULL, IDC_WAIT);
    if (_waitCursor != NULL)
      _oldCursor = SetCursor(_waitCursor);
  }
  ~CWaitCursor()
  {
    if (_waitCursor != NULL)
      SetCursor(_oldCursor);
  }
};

INT_PTR CProgressDialog2::DoModalAndWait(const CString &title, HANDLE hEvent, HWND hWndParent,
	LPARAM dwInitParam)
{
  INT_PTR res = 0;
  try
  {
    if (WaitMode)
    {
      CWaitCursor waitCursor;
      //HANDLE h[] = { thread, _createDialogEvent };
	  HANDLE h[] = { hEvent, _createDialogEvent };
      
      DWORD res = WaitForMultipleObjects(sizeof(h) / sizeof(h[0]), h, FALSE,1000);
      if (res == WAIT_OBJECT_0 && !Sync.ThereIsMessage())
        return 0;
    }
    _title = title;
    res = baseClass::DoModal();
  }
  catch(...)
  {
    _wasCreated = true;
    _dialogCreatedEvent.SetEvent();
    res = res;
  }

  ::WaitForSingleObject(hEvent, INFINITE);
  if (!MessagesDisplayed)
    MessageBox( _T("Progress Error"), _T("7-Zip"), MB_ICONERROR | MB_OK);
  return res;
}


bool CProgressDialog2::OnExternalCloseMessage()
{
  UpdateStatInfo(true);
  
  GetDlgItem(IDC_BUTTON_PROGRESS_PRIORITY).ShowWindow(SW_HIDE);
  GetDlgItem(IDC_BUTTON_PAUSE).ShowWindow(SW_HIDE);
  GetDlgItem(IDCANCEL).SetWindowText(Lang::LangStringSpec(IDS_CLOSE, 0x02000713));  
    
  bool thereAreMessages;
  CString okMessage;
  CString okMessageTitle;
  CString errorMessage;
  CString errorMessageTitle;
  {
    Common::System::Threading::CComScopeAutoLock lock(Sync._cs);
    errorMessage = Sync.ErrorMessage;
    errorMessageTitle = Sync.ErrorMessageTitle;
    okMessage = Sync.OkMessage;
    okMessageTitle = Sync.OkMessageTitle;
	thereAreMessages = !Sync.Messages.empty();
  }
  if (!errorMessage.IsEmpty())
  {
    MessagesDisplayed = true;
    if (errorMessageTitle.IsEmpty())
      errorMessageTitle = _T("7-Zip");
    MessageBox(errorMessage, errorMessageTitle, MB_ICONERROR | MB_OK);
  }
  else if (!thereAreMessages)
  {
    MessagesDisplayed = true;
    if (!okMessage.IsEmpty())
    {
      if (okMessageTitle.IsEmpty())
        okMessageTitle = _T("7-Zip");
      MessageBox(okMessage, okMessageTitle, MB_OK);
    }
  }

  if (thereAreMessages && !_cancelWasPressed)
  {
    _waitCloseByCancelButton = true;
    UpdateMessagesDialog();
    return true;
  }

  EndDialog(IDCANCEL);
  return TRUE;
}

LRESULT CProgressDialog2::OnCloseMsg(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
	KillTimer(_timer);
	_timer = 0;
	if (_inCancelMessageBox)
	{
		_externalCloseMessageWasReceived = true;
		return TRUE;
	}
	return OnExternalCloseMessage();
}

void CProgressDialog2::SetTitleText()
{
  CString title;
  if (Sync.GetPaused())
  {
    title = pausedString;
    title += _T(' ');
  }
  if (_prevPercentValue != (UINT32)-1)
  {
    TCHAR s[64];
    _ui64tot(_prevPercentValue, s, 10);
    title += s;
    title += _T('%');
  }
  if (!_foreground)
  {
    title += _T(' ');
    title += backgroundedString;
  }
  title += _T(' ');
  CString totalTitle = title + _title;
  CString fileName;
  Sync.GetTitleFileName(fileName);
  if (!fileName.IsEmpty())
  {
    ReduceString(fileName, kTitleFileNameSizeLimit);
    totalTitle += _T(' ');
    totalTitle += fileName;
  }
  SetWindowText(totalTitle);
  #ifndef _SFX
  AddToTitle(title + MainAddTitle);
  #endif
}

void CProgressDialog2::SetPauseText()
{
	GetDlgItem(IDC_BUTTON_PAUSE).SetWindowText(
		Sync.GetPaused() ?
    continueString : pauseString);
  SetTitleText();
}

LRESULT CProgressDialog2::OnPauseButton(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
  bool paused = !Sync.GetPaused();
  Sync.SetPaused(paused);
  UINT32 curTime = ::GetTickCount();
  if (paused)
    _elapsedTime += (curTime - _prevTime);
  _prevTime = curTime;
  SetPauseText();
  return 0L;
}

void CProgressDialog2::SetPriorityText()
{
	GetDlgItem(IDC_BUTTON_PAUSE).SetWindowText(_foreground ?
      backgroundString :
      foregroundString);
  SetTitleText();
}

LRESULT CProgressDialog2::OnPriorityButton(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
  _foreground = !_foreground;
  #ifndef UNDER_CE
  SetPriorityClass(GetCurrentProcess(), _foreground ?
    NORMAL_PRIORITY_CLASS: IDLE_PRIORITY_CLASS);
  #endif
  SetPriorityText();
  return 0L;
}

void CProgressDialog2::AddMessageDirect(LPCTSTR message)
{
  int itemIndex = _messageList.GetItemCount();
  TCHAR sz[32];
  _i64tot(itemIndex, sz, 10);
  _messageList.InsertItem(itemIndex, sz);
  _messageList.SetItemData(itemIndex, itemIndex);
  _messageList.SetItemText(itemIndex, 1, message);
}

void CProgressDialog2::AddMessage(LPCTSTR message)
{
  CString s = message;
  while (!s.IsEmpty())
  {
    int pos = s.Find(_T('\n'));
    if (pos < 0)
      break;
    AddMessageDirect(s.Left(pos));
    s.Delete(0, pos + 1);
  }
  AddMessageDirect(s);
}

static unsigned GetNumDigits(UINT32 value)
{
  unsigned i;
  for (i = 0; value >= 10; i++)
    value /= 10;
  return i;
}

void CProgressDialog2::UpdateMessagesDialog()
{
  CStringVector messages;
  {
    Common::System::Threading::CComScopeAutoLock lock(Sync._cs);
    for (size_t i = _numPostedMessages; i < Sync.Messages.size(); i++)
		messages.push_back(Sync.Messages[i]);
    _numPostedMessages = Sync.Messages.size();
  }
  if (!messages.empty())
  {
    for (size_t i = 0; i < messages.size(); i++)
      AddMessage(messages[i]);
    if (_numAutoSizeMessages < 256 || GetNumDigits(_numPostedMessages) > GetNumDigits(_numAutoSizeMessages))
    {
      _messageList.SetColumnWidth(0, LVSCW_AUTOSIZE);
      _messageList.SetColumnWidth(1, LVSCW_AUTOSIZE);
      _numAutoSizeMessages = _numPostedMessages;
    }
  }
}

LRESULT CProgressDialog2::OnCancel(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
{
	  if (_waitCloseByCancelButton)
      {
        MessagesDisplayed = true;
        EndDialog(wID);
        return 0;
      }
        
      bool paused = Sync.GetPaused();
      if (!paused)
        OnPauseButton(wNotifyCode, wID, hWndCtl, bHandled);
      _inCancelMessageBox = true;
      int res = ::MessageBoxW(HWND(*this),
          Lang::LangStringSpec(IDS_PROGRESS_ASK_CANCEL, 0x02000C30),
          _title, MB_YESNOCANCEL);
      _inCancelMessageBox = false;
      if (!paused)
        OnPauseButton(wNotifyCode, wID, hWndCtl, bHandled);
      if (res == IDCANCEL || res == IDNO)
      {
        if (_externalCloseMessageWasReceived)
          OnExternalCloseMessage();
        return true;
      }

      _cancelWasPressed = true;
      MessagesDisplayed = true;
      
		Sync.SetStopped(true);
		EndDialog(wID);
		return 0;
}


void CProgressDialog2::CheckNeedClose()
{
  if (_needClose)
  {
    PostMessage(kCloseMessage);
    _needClose = false;
  }
}

void CProgressDialog2::ProcessWasFinished()
{
  // Set Window title here.
  if (!WaitMode)
    WaitCreating();
  
  if (_wasCreated)
    PostMessage(kCloseMessage);
  else
    _needClose = true;
}

 void WINAPI CProgressThreadVirt::MyThreadFunction(LPVOID param)
  {
    CProgressThreadVirt *p = (CProgressThreadVirt *)param;
    try
    {
      p->Process();
      p->ThreadFinishedOK = true;
    }
    catch (...) { p->Result = E_FAIL; }
  }

HRESULT CProgressThreadVirt::Create(const CString &title, HWND parentWindow)
{
	ThreadPool->AddHighPriorityTask(MyThreadFunction, this, ProgressDialog.GetThreadFinishHandle());
	ProgressDialog.DoModalAndWait(title, ProgressDialog.GetThreadFinishHandle(), parentWindow);
	
  return S_OK;
}

CString HResultToMessage(HRESULT errorCode)
{
	CString message;
	if (errorCode == E_OUTOFMEMORY)
		message = Lang::LangStringSpec(IDS_MEM_ERROR, 0x0200060B);
	else if (!Error::FormatMessage(errorCode, message))
		message.Empty();
	if (message.IsEmpty())
		message = _T("Error");
	return message;
}

static void AddMessageToString(CString &dest, const CString &src)
{
  if (!src.IsEmpty())
  {
    if (!dest.IsEmpty())
      dest += _T('\n');
    dest += src;
  }
}

void CProgressThreadVirt::Process()
{
  CProgressCloser closer(ProgressDialog);
  CString m;
  try { Result = ProcessVirt(); }
  catch(const wchar_t *s) { m = s; }
  catch(const CString &s) { m = s; }
  catch(const char *s) { m = String::GetUnicodeString(s); }
  catch(...) { m = _T("Error"); }
  if (Result != E_ABORT)
  {
    if (m.IsEmpty() && Result != S_OK)
      m = HResultToMessage(Result);
  }
  AddMessageToString(m, ErrorMessage);
  AddMessageToString(m, ErrorPath1);
  AddMessageToString(m, ErrorPath2);

  if (m.IsEmpty())
  {
    if (!OkMessage.IsEmpty())
    {
      ProgressDialog.Sync.SetOkMessageTitle(OkMessageTitle);
      ProgressDialog.Sync.SetOkMessage(OkMessage);
    }
  }
  else
  {
    ProgressDialog.Sync.SetErrorMessage(m);
    if (Result == S_OK)
      Result = E_FAIL;
  }
}
